#include "Model.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <iostream>
#include <util.h>

#include <Singleton.h>

using namespace std;
using namespace Assimp;

static inline glm::vec3 vec3_cast(const aiVector3D& v)
{
    return { v.x, v.y, v.z };
}

static inline glm::vec2 vec2_cast(const aiVector2D& v)
{
    return { v.x, v.y };
}

static inline glm::quat quat_cast(const aiQuaternion& q)
{
    return { q.w, q.x, q.y, q.z };
}

static inline glm::mat4 mat4_cast(const aiMatrix4x4& mat)
{
    return glm::transpose(glm::make_mat4(&mat.a1));
}

static inline glm::mat3 mat3_cast(const aiMatrix3x3& mat)
{
    return glm::transpose(glm::make_mat3(&mat.a1));
}

ModelAsset::ModelAsset(const string& path)
{
    LoadModel(path);
}

void ModelAsset::LoadModel(const string& path)
{
    filePath = path;
    Importer importer;
    const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);

    if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
        cerr << "ERROR::ASSIMP::" << importer.GetErrorString() << endl;
        return;
    }
    directory = path.substr(0, path.find_last_of('/'));

    LoadMeshes(scene);

    for (auto &mesh: meshes){
        if (mesh.name.find("body-link") == 0) {
            CreateCombinedMesh();
        }
        // if (mesh.name.find("wb-") != string::npos){
        //     CreateCombinedMesh(true, mesh.name);
        // }
        // 如果mesh的名字以wb-开头，则将其合并为一个mesh
        if (mesh.name.find("wb-") == 0){
            CreateCombinedMesh(true, mesh.name);
        }
    }


    boneIdtoNames.clear();
    for (auto &b : boneNameIndex)
    {
        boneIdtoNames.insert({b.second, b.first});
    }

    LoadAnimations(scene);

    cout << "~~~~~~Load ModelAsset~~~~~~" << endl;

    cout << scene->mRootNode->mName.C_Str() << endl;
    cout << "Path: " << path << endl;
    if (!boneNameIndex.empty()) {
        cout << "Bones:" << endl;
        for (auto bone : boneNameIndex) {
            cout << "\t" << bone.second << ": " << bone.first << endl;
        }
        cout << endl;
    }

    // SkeletonAsset
    for (int j = 0; j < scene->mRootNode->mNumChildren; ++j) {
        // cout << "Child Name:" << scene->mRootNode->mChildren[j]->mName.C_Str() << endl;
        if (boneNameIndex.find(string(scene->mRootNode->mChildren[j]->mName.C_Str())) != boneNameIndex.end()) {
            SkeletonName = scene->mRootNode->mName.C_Str();
            if (SkeletonName.empty() || SkeletonName == "") {
                SkeletonName = "Skeleton";
            }

            if (skeleton == nullptr) {
                skeleton.reset(new SkeletonAsset(SkeletonName, scene->mRootNode->mChildren[j], this));
            }

            cout << endl;
        } else {
            for (int n = 0; n < scene->mRootNode->mChildren[j]->mNumChildren; n++) {
                if (boneNameIndex.find(string(scene->mRootNode->mChildren[j]->mChildren[n]-> mName.C_Str())) != boneNameIndex.end()) {

                    SkeletonName = scene->mRootNode->mChildren[j]->mName.C_Str();
                    if (SkeletonName.empty() || SkeletonName == "") {
                        SkeletonName = "Skeleton";
                    }

                    if (skeleton == nullptr) {
                        skeleton.reset(new SkeletonAsset(SkeletonName, scene->mRootNode->mChildren[j]->mChildren[n], this));
                    }

                    cout << endl;
                }
            }
        }
    }

    if (skeleton == nullptr)
        std::cout << "Skeleton is Null !  can't find skeleton" << endl;

    
    cout << "_______Start OrderAnimation________" << endl;
    // 遍历所有动画，转换为SkeletonAsset的orderedAnimation
    // for (auto anim : cachedAnim){
    //     anim.second->OrderAnimation(skeleton);
    // }

    for (auto anim:animations){
        anim->OrderAnimation(skeleton);
    }

    cout << "_______End OrderAnimation________" << endl;

    // for (auto animName: (*animNames)){
    //     auto anim = cachedAnim.GetOrNull(animName);
    //     if (anim.value() != nullptr)
    //     {
    //         anim.value()->OrderAnimation(skeleton);
    //     }
    // }

    // std::cout << "model: "<< (skeleton->SkeletonType.c_str()) << endl;

    // 遍历所有mesh，转换为SkeletonAsset的orderedMesh
    for (auto mesh : meshes){
        mesh.OrderBindedMesh(skeleton);
    }

    for (auto& om :skeleton->orderedMeshes){
        if (om.second->hasBodyPart){
            if (skeleton->orderedMeshes.find("part_head") == skeleton->orderedMeshes.end()){
                throw std::runtime_error("part_head mesh is not found");
            }

            om.second->textureBody = skeleton->orderedMeshes["part_head"]->texture;
        }
    }

    cout << "_______End Load________" << endl;

    cout << endl;
}

Texture ModelAsset::LoadMaterialTextures(const aiScene* scene, aiMaterial* material, aiTextureType type, const string& typeName)
{
    Texture r;
    for (int i = 0; i < material->GetTextureCount(type); ++i) {
        aiString path;
        material->GetTexture(type, i, &path);
        string imagePath = directory + "/" + path.C_Str();
        auto t = Singleton<Cache<string, Texture>>::Instance().GetOrNull(imagePath);
        if (t.has_value()) {
            return t.value();
        }
        uint32_t texId = util::TextureFromFile(imagePath);
        if (texId == 0) {
            continue;
        }
        Texture texture;
        texture.Index = i;
        texture.TexId = texId;
        texture.Uid = imagePath;
        Singleton<Cache<string, Texture>>::Instance().AddOrUpdate(imagePath, texture);
        return texture;
    }
    return { "", 0, 0 };
}

const aiNodeAnim* ModelAsset::FindNodeAnim(const aiAnimation* animation, const string& nodeName)
{
    for (uint32_t i = 0; i < animation->mNumChannels; i++) {
        const aiNodeAnim* nodeAnim = animation->mChannels[i];
        if (string(nodeAnim->mNodeName.data) == nodeName)
            return nodeAnim;
    }
    return nullptr;
}

static double CalcP(double i, double start, double end)
{
    return (i - start) / (end - start);
}

static glm::mat4 CalcInterpolatedRotation(float animationTime, const aiNodeAnim* nodeAnim)
{
    auto findRotation = [](float animationTime, const aiNodeAnim* nodeAnim) {
        for (uint32_t i = 0; i < nodeAnim->mNumRotationKeys - 1; i++)
            if (animationTime < (float)nodeAnim->mRotationKeys[i + 1].mTime)
                return i;
        return nodeAnim->mNumRotationKeys - 1;
    };

    if (nodeAnim->mNumRotationKeys == 1)
        return { mat3_cast(nodeAnim->mRotationKeys[0].mValue.GetMatrix()) };
    if (animationTime < nodeAnim->mRotationKeys[0].mTime)
        return { mat3_cast(nodeAnim->mRotationKeys[0].mValue.GetMatrix()) };
    if (animationTime > nodeAnim->mRotationKeys[nodeAnim->mNumRotationKeys - 1].mTime)
        return { mat3_cast(nodeAnim->mRotationKeys[nodeAnim->mNumRotationKeys - 1].mValue.GetMatrix()) };
    auto index = findRotation(animationTime, nodeAnim);
    auto p = (float)CalcP(animationTime, nodeAnim->mRotationKeys[index].mTime, nodeAnim->mRotationKeys[index + 1].mTime);
    auto& start = nodeAnim->mRotationKeys[index].mValue;
    auto& end = nodeAnim->mRotationKeys[index + 1].mValue;
    aiQuaternion out;
    aiQuaternion::Interpolate(out, start, end, p);
    return { mat3_cast(out.Normalize().GetMatrix()) };
}

static glm::mat4 CalcInterpolatedPosition(float animationTime, const aiNodeAnim* nodeAnim)
{
    auto findPosition = [](float animationTime, const aiNodeAnim* nodeAnim) {
        for (uint32_t i = 0; i < nodeAnim->mNumPositionKeys - 1; i++)
            if (animationTime < (float)nodeAnim->mPositionKeys[i + 1].mTime)
                return i;
        return nodeAnim->mNumPositionKeys - 1;
    };

    if (nodeAnim->mNumPositionKeys == 1)
        return glm::translate(glm::mat4(1.0f), vec3_cast(nodeAnim->mPositionKeys[0].mValue));
    if (animationTime < nodeAnim->mPositionKeys[0].mTime)
        return glm::translate(glm::mat4(1.0f), vec3_cast(nodeAnim->mPositionKeys[0].mValue));
    if (animationTime > nodeAnim->mPositionKeys[nodeAnim->mNumPositionKeys - 1].mTime)
        return glm::translate(glm::mat4(1.0f), vec3_cast(nodeAnim->mPositionKeys[nodeAnim->mNumPositionKeys - 1].mValue));
    auto index = findPosition(animationTime, nodeAnim);
    auto p = (float)CalcP(animationTime, nodeAnim->mPositionKeys[index].mTime, nodeAnim->mPositionKeys[index + 1].mTime);
    auto& start = nodeAnim->mPositionKeys[index].mValue;
    auto& end = nodeAnim->mPositionKeys[index + 1].mValue;
    return glm::translate(glm::mat4(1.0f), vec3_cast(start + p * (end - start)));
}

static glm::mat4 CalcInterpolatedScaling(float animationTime, const aiNodeAnim* nodeAnim)
{
    auto findScaling = [](float animationTime, const aiNodeAnim* nodeAnim) {
        for (uint32_t i = 0; i < nodeAnim->mNumScalingKeys - 1; i++)
            if (animationTime < (float)nodeAnim->mScalingKeys[i + 1].mTime)
                return i;
        return nodeAnim->mNumScalingKeys - 1;
    };

    if (nodeAnim->mNumScalingKeys == 1)
        return glm::scale(glm::mat4(1.0f), vec3_cast(nodeAnim->mScalingKeys[0].mValue));
    if (animationTime < nodeAnim->mScalingKeys[0].mTime)
        return glm::scale(glm::mat4(1.0f), vec3_cast(nodeAnim->mScalingKeys[0].mValue));
    if (animationTime > nodeAnim->mScalingKeys[nodeAnim->mNumScalingKeys - 1].mTime)
        return glm::scale(glm::mat4(1.0f), vec3_cast(nodeAnim->mScalingKeys[nodeAnim->mNumScalingKeys - 1].mValue));
    auto index = findScaling(animationTime, nodeAnim);
    auto p = (float)CalcP(animationTime, nodeAnim->mScalingKeys[index].mTime, nodeAnim->mScalingKeys[index + 1].mTime);
    auto& start = nodeAnim->mScalingKeys[index].mValue;
    auto& end = nodeAnim->mScalingKeys[index + 1].mValue;
    return glm::scale(glm::mat4(1.0f), vec3_cast(start + p * (end - start)));
}

void ModelAsset::NodeAnimInner(aiAnimation *anim, float animationTime, const aiNode *node,
                          std::vector<SRT> &animSRTs, std::vector<glm::mat4> &animMat4s)
{
    auto nodeName = string(node->mName.C_Str());
    auto nodeTransformation = mat4_cast(node->mTransformation);
    auto nodeAnim = FindNodeAnim(anim, nodeName);

    if (nodeAnim) {
        auto sT = CalcInterpolatedScaling(animationTime, nodeAnim);
        auto rT = CalcInterpolatedRotation(animationTime, nodeAnim);
        auto pT = CalcInterpolatedPosition(animationTime, nodeAnim);
        nodeTransformation = pT * rT * sT;
    }

    // nodeTransformation = nodeTransformation * glm::inverse(mat4_cast(node->mTransformation));

    if (boneNameIndex.find(nodeName) != boneNameIndex.end()) {
        uint32_t BoneIndex = boneNameIndex[nodeName];

        // 不应该对骨骼应用animGlobalInverseT，具体原因窝还不理解
        // 但是如果应用了会导致次级骨骼transform出现异常
        animSRTs[BoneIndex] = SRT::Mat4ToSRT(nodeTransformation);//SRT::Mat4ToSRT(animGlobalInverseT * nodeTransformation);
        animMat4s[BoneIndex] = nodeTransformation;//animGlobalInverseT * nodeTransformation;
    }

    for (uint32_t i = 0; i < node->mNumChildren; i++) {
        NodeAnimInner(anim, animationTime, node->mChildren[i], animSRTs, animMat4s);
    }
}

void ModelAsset::BoneTransform(const aiNode *root, aiAnimation *anim, float timeInMs,
                          std::vector<SRT> &animSRTs, std::vector<glm::mat4> &animMat4s)
{
    // 不论动画如何，我们在这里都生成了全部骨骼变化的数组
    // 实际上，一些动画并不是所有骨骼都有运动（而是保持在restPose）
    // 这里我们应该想办法进行优化

    animSRTs.resize(boneNameIndex.size(), SRT());
    animMat4s.resize(boneNameIndex.size(), glm::mat4(1.0f));

    auto ticksPerSecond = (float)(anim->mTicksPerSecond != 0 ? anim->mTicksPerSecond : 25.0f);
    auto timeInTicks = timeInMs * ticksPerSecond / 1000;
    auto animationTime = fmod(timeInTicks, (float)anim->mDuration);

    NodeAnimInner(anim, animationTime, root, animSRTs, animMat4s);
}

void ModelAsset::LoadAnimations(const aiScene* scene)
{
    animNames = std::make_shared<std::vector<std::string>>(scene->mNumAnimations);
    auto keyDuration = 33.33f;
    auto boneCounter = boneNameIndex.size();
    for (int i = 0; i < scene->mNumAnimations; ++i) {
        auto& anim = scene->mAnimations[i];
        auto animName = string(anim->mName.C_Str());
        
        auto ticksPerSecond = (float)(anim->mTicksPerSecond != 0 ? anim->mTicksPerSecond : 25.0f);
        auto duration = anim->mDuration * 1000 / ticksPerSecond;
        auto animFrameCount = int(duration / keyDuration) + 1;

        vector<shared_ptr<vector<SRT>>> frames;
        vector<shared_ptr<vector<glm::mat4>>> frameMat4s;

        frames.reserve(animFrameCount + 1);
        frameMat4s.reserve(animFrameCount + 1);

        {
            animGlobalInverseT = mat4_cast(scene->mRootNode->mTransformation);
            animGlobalInverseT = glm::inverse(animGlobalInverseT);
        }

        //cout << animName << ": " << animFrameCount << ", " << duration << ", " << keyDuration << endl;

        for (float j = 0; j < duration;) {
            auto ts = make_shared<vector<SRT>>();
            auto tm4s = make_shared<vector<glm::mat4>>();
            BoneTransform(scene->mRootNode, anim, j, *ts, *tm4s);
            frames.emplace_back(ts);
            frameMat4s.emplace_back(tm4s);

            j += keyDuration;
        }

        // if (cachedAnim.find(animName) == cachedAnim.end()) {
        //     cachedAnim.insert(std::make_pair(animName, make_shared<AnimationAsset>(frames, boneIdtoNames, animName)));
        // }
        (*animNames)[i] = animName;
        animations.push_back(make_shared<AnimationAsset>(frames, boneIdtoNames, animName));
    }
}

void ModelAsset::LoadMeshes(const aiScene* scene)
{
    for (int i = 0; i < scene->mNumMeshes; ++i)
    {
        auto &mesh = scene->mMeshes[i];
        if (mesh->mName.C_Str() != "body-link")
        {
            continue;
        }
        std::cout << "body-link mesh name " << mesh->mName.C_Str() << std::endl;

        vector<Vertex> vertices;
        vertices.resize(mesh->mNumVertices);
        vector<uint32_t> indices;
        indices.reserve(mesh->mNumFaces * 3);
        Texture texture;

        for (int j = 0; j < mesh->mNumVertices; ++j)
        {
            auto &v = vertices[j];
            v.Position.x = mesh->mVertices[j].x;
            v.Position.y = mesh->mVertices[j].y;
            v.Position.z = mesh->mVertices[j].z;

            if (mesh->mNormals != nullptr)
            {
                v.Normal.x = mesh->mNormals[j].x;
                v.Normal.y = mesh->mNormals[j].y;
                v.Normal.z = mesh->mNormals[j].z;
            }

            if (mesh->mTextureCoords[0])
            {
                v.TexCoords.x = mesh->mTextureCoords[0][j].x;
                v.TexCoords.y = mesh->mTextureCoords[0][j].y;
            }
            else
            {
                v.TexCoords.x = 0;
                v.TexCoords.y = 0;
            }
        }

        for (int j = 0; j < mesh->mNumFaces; ++j)
        {
            const aiFace &face = mesh->mFaces[j];
            for (int k = 0; k < face.mNumIndices; ++k)
                indices.emplace_back(face.mIndices[k]);
        }

        if (mesh->mMaterialIndex >= 0)
        {
            aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
            texture = LoadMaterialTextures(scene, material, aiTextureType_DIFFUSE, "base");
        }

        for (int j = 0; j < mesh->mNumBones; ++j)
        {
            auto &bone = mesh->mBones[j];
            auto boneName = string(bone->mName.C_Str());

            // if (j == 0) {
            //     cout << mesh->mName.C_Str() << " mBone[0]" << boneName << endl;
            // }

            uint32_t boneId = 0;
            {
                auto iter = boneNameIndex.find(boneName);
                if (iter == boneNameIndex.end())
                {
                    boneId = boneNameIndex.size();
                    boneNameIndex.insert({boneName, boneId});
                }
                else
                {
                    boneId = iter->second;
                }
            }
            auto m = mat4_cast(bone->mOffsetMatrix);
            // auto m = mat4_cast(bone->mNode->mTransformation);

            boneOffsetCache.insert({boneId, m});
            for (int k = 0; k < bone->mNumWeights; ++k)
            {
                auto &widget = bone->mWeights[k];
                auto &v = vertices[widget.mVertexId];
                for (int l = 0; l < MAX_BONE_LENGTH; ++l)
                {
                    if (v.BoneWeight[l] == 0.0f)
                    {
                        v.BoneId[l] = boneId;
                        v.BoneWeight[l] = widget.mWeight;
                        break;
                    }
                    // if (l == MAX_BONE_LENGTH - 1)
                    //     cerr << "more bone sort needed!" << endl;
                }
            }
        }
        std::unordered_map<int, std::string> IdtoNames;
        for (auto &b : boneNameIndex)
        {
            IdtoNames.insert({b.second, b.first});
        }

        meshes.emplace_back(MeshAsset(mesh->mName.C_Str(), vertices, indices, texture, IdtoNames));
        break;
    }

    for (int i = 0; i < scene->mNumMeshes; ++i) {
        auto& mesh = scene->mMeshes[i];
        if (mesh->mName.C_Str() == "body-link")
        {
            continue;
        }
        std::cout << "mesh name " << mesh->mName.C_Str() << std::endl;
        std::string meshName = mesh->mName.C_Str();
        bool isPart = false;
        // 如果这个mesh的名字中有part_，那么就是身体的部分，应该用身体对应位置的顶点的法线作为这个顶点的法线
        if (meshName.find("part_") != std::string::npos && meshes.size() > 0 && meshes[0].name == "body-link")
        {
            isPart = true;
        }

        vector<Vertex> vertices;
        vertices.resize(mesh->mNumVertices);
        vector<uint32_t> indices;
        indices.reserve(mesh->mNumFaces * 3);
        Texture texture;

        for (int j = 0; j < mesh->mNumVertices; ++j) {
            auto& v = vertices[j];
            v.Position.x = mesh->mVertices[j].x;
            v.Position.y = mesh->mVertices[j].y;
            v.Position.z = mesh->mVertices[j].z;
            
            if (isPart){
                bool finded = false;
                // 找到和当前顶点坐标一致的body-link的顶点
                for (int k = 0; k < meshes[0].vertices.size(); ++k) {
                    auto &v2 = meshes[0].vertices[k];
                    if (v.Position == v2.Position)
                    {
                        v.Normal = v2.Normal;
                        finded = true;
                        break;
                    }
                }
                if (!finded) {
                    // std::cerr << "can't find body-link vertex" << std::endl;
                    if (mesh->mNormals != nullptr)
                    {
                        v.Normal.x = mesh->mNormals[j].x;
                        v.Normal.y = mesh->mNormals[j].y;
                        v.Normal.z = mesh->mNormals[j].z;
                    }
                }
            }
            else if(mesh->mNormals != nullptr)
            {
                v.Normal.x = mesh->mNormals[j].x;
                v.Normal.y = mesh->mNormals[j].y;
                v.Normal.z = mesh->mNormals[j].z;
            }

            if (mesh->mTextureCoords[0]) {
                v.TexCoords.x = mesh->mTextureCoords[0][j].x;
                v.TexCoords.y = mesh->mTextureCoords[0][j].y;
            } else {
                v.TexCoords.x = 0;
                v.TexCoords.y = 0;
            }
        }

        for (int j = 0; j < mesh->mNumFaces; ++j) {
            const aiFace& face = mesh->mFaces[j];
            for (int k = 0; k < face.mNumIndices; ++k)
                indices.emplace_back(face.mIndices[k]);
        }

        if (mesh->mMaterialIndex >= 0) {
            aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
            texture = LoadMaterialTextures(scene, material, aiTextureType_DIFFUSE, "base");
        }

        for (int j = 0; j < mesh->mNumBones; ++j) {
            auto& bone = mesh->mBones[j];
            auto boneName = string(bone->mName.C_Str());

                // if (j == 0) {
                //     cout << mesh->mName.C_Str() << " mBone[0]" << boneName << endl;
                // }

                uint32_t boneId = 0;
            {
                auto iter = boneNameIndex.find(boneName);
                if (iter == boneNameIndex.end()) {
                    boneId = boneNameIndex.size();
                    boneNameIndex.insert({ boneName, boneId });
                } else {
                    boneId = iter->second;
                }
            }
            auto m = mat4_cast(bone->mOffsetMatrix);
            //auto m = mat4_cast(bone->mNode->mTransformation);
            
            boneOffsetCache.insert({ boneId, m });
            for (int k = 0; k < bone->mNumWeights; ++k) {
                auto& widget = bone->mWeights[k];
                auto& v = vertices[widget.mVertexId];
                for (int l = 0; l < MAX_BONE_LENGTH; ++l) {
                    if (v.BoneWeight[l] == 0.0f) {
                        v.BoneId[l] = boneId;
                        v.BoneWeight[l] = widget.mWeight;
                        break;
                    }
                    // if (l == MAX_BONE_LENGTH - 1)
                    //     cerr << "more bone sort needed!" << endl;
                }
            }
        }
        std::unordered_map<int,std::string> IdtoNames;
        for (auto& b : boneNameIndex) {
            IdtoNames.insert({ b.second, b.first });
        }

        meshes.emplace_back(MeshAsset(mesh->mName.C_Str() ,vertices, indices, texture, IdtoNames));
    }
}

void ModelAsset::CreateCombinedMesh(bool combineCloth, std::string clothName)
{

    std::string meshName = combineCloth ? clothName + "combined" : "combined";

    vector<Vertex> vertices;
    vector<uint32_t> indices;
    Texture texture;

    std::unordered_map<int, std::string> IdtoNames;
    for (auto &b : boneNameIndex)
    {
        IdtoNames.insert({b.second, b.first});
    }

    for (auto &mesh: meshes)
    {
        bool hasMesh = false;
        uint32_t renderMask = 0;
        if (combineCloth && mesh.name == clothName)
        {
            // rendermask 第10位置1，表示渲染服装
            renderMask = 512;
            hasMesh = true;
        }
        else if (mesh.name.find("part_") != std::string::npos)
        {
            if (!combineCloth)
            {
                if (mesh.name.find("part_head") != std::string::npos)
                {
                    // rendermask 第一位置1，表示渲染头部
                    renderMask = 1;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_torso") != std::string::npos)
                {
                    // rendermask 第二位置1，表示渲染躯干
                    renderMask = 2;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_hip") != std::string::npos)
                {
                    // rendermask 第三位置1，表示渲染臀部
                    renderMask = 4;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_thigh") != std::string::npos)
                {
                    // rendermask 第四位置1，表示渲染大腿
                    renderMask = 8;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_leg") != std::string::npos)
                {
                    // rendermask 第五位置1，表示渲染小腿
                    renderMask = 16;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_foot") != std::string::npos)
                {
                    // rendermask 第六位置1，表示渲染脚部
                    renderMask = 32;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_upperarm") != std::string::npos)
                {
                    // rendermask 第七位置1，表示渲染上臂
                    renderMask = 64;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_lowerarm") != std::string::npos)
                {
                    // rendermask 第八位置1，表示渲染前臂
                    renderMask = 128;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_hand") != std::string::npos)
                {
                    // rendermask 第九位置1，表示渲染手腕
                    renderMask = 256;
                    hasMesh = true;
                }
                else
                {

                    std::cout << "part not found: name: " << mesh.name << std::endl;
                    continue;
                }
            }
            else{
                if (mesh.name.find("part_c_head-" + clothName) != std::string::npos)
                {
                    // rendermask 第一位置1，表示渲染头部
                    renderMask = 1;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_torso-" + clothName) != std::string::npos)
                {
                    // rendermask 第二位置1，表示渲染躯干
                    renderMask = 2;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_hip-" + clothName) != std::string::npos)
                {
                    // rendermask 第三位置1，表示渲染臀部
                    renderMask = 4;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_thigh-" + clothName) != std::string::npos)
                {
                    // rendermask 第四位置1，表示渲染大腿
                    renderMask = 8;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_leg-" + clothName) != std::string::npos)
                {
                    // rendermask 第五位置1，表示渲染小腿
                    renderMask = 16;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_foot-" + clothName) != std::string::npos)
                {
                    // rendermask 第六位置1，表示渲染脚部
                    renderMask = 32;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_upperarm-" + clothName) != std::string::npos)
                {
                    // rendermask 第七位置1，表示渲染上臂
                    renderMask = 64;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_lowerarm-" + clothName) != std::string::npos)
                {
                    // rendermask 第八位置1，表示渲染前臂
                    renderMask = 128;
                    hasMesh = true;
                }
                else if (mesh.name.find("part_c_hand-" + clothName) != std::string::npos)
                {
                    // rendermask 第九位置1，表示渲染手腕
                    renderMask = 256;
                    hasMesh = true;
                }
                else
                {
                    // 没有对应体块 报错
                    std::cout << "cloth part not found: name: " << mesh.name << std::endl;
                    continue;
                }
            }
        }

        if (hasMesh){
            int offset = vertices.size();
            for (int j = 0; j < mesh.vertices.size(); ++j)
            {
                Vertex v = mesh.vertices[j];
                v.RenderMask = renderMask;

                for (int k = 0; k < MAX_BONE_LENGTH; ++k)
                {
                    if (mesh.boneIdtoNames.find(v.BoneId[k]) != mesh.boneIdtoNames.end())
                    {
                        if (boneNameIndex.find(mesh.boneIdtoNames[v.BoneId[k]]) != boneNameIndex.end())
                        {
                            v.BoneId[k] = boneNameIndex[mesh.boneIdtoNames[v.BoneId[k]]];
                        }
                        else
                        {
                            throw std::runtime_error("CreateCombinedMesh: boneNameIndex: bone not found");
                        }
                    }
                    else
                    {
                        throw std::runtime_error("CreateCombinedMesh: boneIdtoNames: bone not found");
                    }
                }

                vertices.emplace_back(v);
            }
            for (int j = 0; j < mesh.indices.size(); ++j)
            {
                indices.emplace_back(mesh.indices[j] + offset);
            }
            texture = mesh.texture;
        }
    }

    std::cout << "Combined " << meshName << ": vertices size: " << vertices.size() << std::endl;
    meshes.emplace_back(MeshAsset(meshName.c_str(), vertices, indices, texture, IdtoNames));
}
